﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dycox.ComponentModel;
using System.ComponentModel;

namespace Dycox.Workflows
{
    public class ConditionTestArguments : Dycox.Utils.IContainerProvider
    {
        internal ConditionTestArguments(IWorkflowComponent component, StageOperationContract contract, Stage stage, IReflectionRoot reflectingObject)
        {
            if (stage == null)
                throw new ArgumentNullException("stgae");

            _component = component;
            _contract = contract;
            _stage = stage;
            _reflectingObject = reflectingObject;
        }

        private IWorkflowComponent _component;

        public IWorkflowComponent Component
        {
            get { return _component; }
        }

        private StageOperationContract _contract;

        public StageOperationContract UseState
        {
            get { return _contract; }
        }

        private Stage _stage;

        public Stage Stage
        {
            get { return _stage; }
        }

        private IReflectionRoot _reflectingObject;

        public IReflectionRoot ReflectionObject
        {
            get { return _reflectingObject; }
        }

        public WFStep GetStep()
        {
            return _component as WFStep;
        }

        public WFThread GetThread()
        {
            WFStep step = GetStep();
            if (step != null)
                return step.Thread;

            if (_component is WFThread)
                return (WFThread)_component;

            return null;
        }

        public WFProcess GetProcess()
        {
            WFThread thread = GetThread();

            if (thread == null)
                return _component as WFProcess;
            else
                return thread.Process;
        }

        object Utils.IContainerProvider.GetContainer(string containerName)
        {
            if (string.IsNullOrEmpty(containerName))
                return ((Utils.IContainerProvider)this).DefaultContainer;

            object container;


            if (containerName[0] == '#')
            {
                IWorkflowComponent component = this.Component;
                containerName = containerName.Substring(1);

                if (component != null && !(component is WFProcess))
                {
                    Workflow wf = _stage.Workflow;
                    WFStep step = GetStep();

                    if (step == null)
                        step = GetThread().ActiveStep;

                    if (step != null)
                    {
                        step = wf.Control.GetNearestCompletedStep(step, containerName);
                        container = step.GetUserState(wf.Stages[step]);
                    }
                    else
                        container = null;

                }
                else
                    container = null;
            }
            else
            {
                switch (containerName.ToLower())
                {
                    case "userstate":
                        container = UseState;
                        break;
                    case "step":
                        container = GetStep();
                        break;
                    case "thread":
                        container = GetThread();
                        break;
                    case "process":
                        container = GetProcess();
                        break;
                    case "app":
                        container = ReflectionObject;
                        break;
                    default:
                        throw new WorkflowException(SR.GetString("container_not_defined", containerName));
                }
            }

            return container;
        }

        object Utils.IContainerProvider.DefaultContainer
        {
            get { return this.UseState; }
        }

        internal static PropertyDescriptor[] ResolveProperties(ICollection<string> names, Stage stage)
        {
            var delimiter = Utils.DataBinderUtil.ContainerDelimiter;
            List<PropertyDescriptor> list = new List<PropertyDescriptor>(names.Count);
            Dictionary<string, PropertyDescriptorCollection> dict = new Dictionary<string, PropertyDescriptorCollection> {
                { "userstate", TypeDescriptor.GetProperties(stage.ContractType) },
                { "step", TypeDescriptor.GetProperties(typeof(WFStep)) },
                { "thread", TypeDescriptor.GetProperties(typeof(WFThread)) },
                { "process", TypeDescriptor.GetProperties(typeof(WFProcess)) }
            };

            Workflow wf = stage.Workflow;
            if (wf is IRequiresWorkflowReflection)
                dict.Add("app", TypeDescriptor.GetProperties(((IRequiresWorkflowReflection)wf).AppObjType));

            foreach (string name in names)
            {
                string pname, cname;
                int index = name.IndexOf(Utils.DataBinderUtil.ContainerDelimiter);
                PropertyDescriptorCollection coll;

                if (index >= 0)
                {
                    pname = name.Substring(index + Utils.DataBinderUtil.ContainerDelimiter.Length);
                    if (index > 0)
                        cname = name.Substring(0, index).ToLower();
                    else
                        cname = "userstate";
                }
                else
                {
                    pname = name;
                    cname = "userstate";
                }

                if (!dict.TryGetValue(cname, out coll))
                {
                    if (cname[0] == '#')
                    {
                        var st = wf.Stages[cname.Substring(1)];
                        if (st != null)
                            dict.Add(cname, TypeDescriptor.GetProperties(st.ContractType));
                    }
                }

                if (coll == null)
                    throw new WorkflowException(SR.GetString("container_not_defined", cname));

                index = pname.IndexOf(Type.Delimiter);
                if (index > 0) pname = pname.Substring(0, index);

                var p = coll.Find(pname, true);
                
                if (p == null)
                    throw new WorkflowException(SR.GetString("property_not_found", name));

                list.Add(p);
            }

            return list.ToArray();
        }

        //private static PropertyDescriptor FindProperty(Type type, string name)
        //{
        //    if (string.IsNullOrEmpty(name)) return null;

        //    string[] array = name.Split(Type.Delimiter);
        //    PropertyDescriptor p = null;

        //    foreach (string n in array)
        //    {
        //        p = TypeDescriptor.GetProperties(type)[n];
        //        if (p == null) break;

        //        type = p.PropertyType;
        //    }

        //    return p;
        //}
    }
}
